package com.example.mq1.mqclient;

import com.example.mq1.common.BinaryTool;
import com.example.mq1.common.MqException;
import com.example.mq1.network.BasicResponse;
import com.example.mq1.network.MessageCallback;
import com.example.mq1.network.Request;
import com.example.mq1.network.Response;
import org.apache.ibatis.reflection.wrapper.MapWrapper;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//public class Connection {
//
//    private Socket socket;
//    //一个 socket 管理多个 channel
//    private ConcurrentHashMap<String, Channel> channelMap = new ConcurrentHashMap<>();
//    private InputStream inputStream;
//    private OutputStream outputStream;
//    private DataOutputStream dataOutputStream;
//    private DataInputStream dataInputStream;
//    private ExecutorService callbackPool = null;
//
//    public Connection(String host, int port) throws IOException {
//        socket = new Socket(host, port);
//
//        inputStream = socket.getInputStream();
//        outputStream = socket.getOutputStream();
//        dataInputStream = new DataInputStream(inputStream);
//        dataOutputStream = new DataOutputStream(outputStream);
//        callbackPool = Executors.newFixedThreadPool(4);
//
//        //使用一个扫描线程
//        Thread scannerThread = new Thread(() -> {
//            while(!socket.isClosed()) {
//                try {
//                    //1.接收传来的响应
//                    Response response = readResponse();
//                    //2.交给对应的 channel 来处理
//                    dispatchResponse(response);
//                } catch (SocketException e) {
//                    System.out.println("[Connection] 连接正常断开！");
//                } catch (IOException | MqException | ClassNotFoundException e) {
//                    System.out.println("[Connection] 连接异常断开！");
//                    e.printStackTrace();
//                }
//            }
//        });
//        scannerThread.start();
//    }
//
//    /**
//     * 关闭上述资源
//     */
//    public void close() {
//        try {
//            callbackPool.shutdownNow();
//            channelMap.clear();
//            inputStream.close();
//            outputStream.close();
//            socket.close();
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }
//    }
//
//    private Response readResponse() throws IOException, MqException {
//        Response response = new Response();
//        response.setType(dataInputStream.readInt());
//        response.setLength(dataInputStream.readInt());
//        byte[] payload = new byte[response.getLength()];
//        int n = dataInputStream.read(payload);
//        if(n != response.getLength()) {
//            throw new MqException("[Connection] 读取响应格式错误！ type=" + response.getType() +
//                    ", expectedLength=" + response.getLength() + ", actualLength=" + n);
//        }
//        response.setPayload(payload);
//        System.out.println("[Connection] 读取响应成功！type=" + response.getType());
//        return response;
//    }
//
//    public void writeRequest(Request request) throws IOException {
//        dataOutputStream.writeInt(request.getType());
//        dataOutputStream.writeInt(request.getLength());
//        dataOutputStream.write(request.getPayload());
//        dataOutputStream.flush();
//        System.out.println("[Connection] 发送请求！ type=" + request.getType() + ", length=" + request.getLength());
//    }
//
//
//    /**
//     * 用来区分是控制请求的响应，还是处理回调的响应
//     * @param response
//     * @throws IOException
//     * @throws ClassNotFoundException
//     */
//    private void dispatchResponse(Response response) throws IOException, ClassNotFoundException, MqException {
//        if(response.getType() == 0xc) {
//            //通过线程池来处理回调
//            MessageCallback messageCallback = (MessageCallback) BinaryTool.fromBytes(response.getPayload());
//            //根据 channelId 找到对应的 Channel 对象
//            Channel channel = channelMap.get(messageCallback.getChannelId());
//            if(channel == null) {
//                throw new MqException("[Connection] 当前 Channel 在客户端不存在！ channelId=" + messageCallback.getChannelId());
//            }
//            callbackPool.submit(() -> {
//                //使用线程池来处理回调
//                try {
//                    channel.getConsumer().handlerDelivery(messageCallback.getConsumerTag(),
//                            messageCallback.getBasicProperties(), messageCallback.getBody());
//                } catch (IOException | MqException e) {
//                    throw new RuntimeException(e);
//                }
//            });
//        } else {
//            BasicResponse basicResponse = (BasicResponse) BinaryTool.fromBytes(response.getPayload());
//            //将这个结果放到 channel 的哈希表中
//            Channel channel = channelMap.get(basicResponse.getChannelId());
//            if(channel == null) {
//                throw new MqException("[Connection] 当前 Channel 在客户端不存在！ channelId=" + basicResponse.getChannelId());
//            }
//            channel.putReturn(basicResponse);
//        }
//    }
//
//    /**
//     * 创建 channel
//     * @return
//     * @throws IOException
//     */
//    public Channel createChannel() throws IOException {
//        //1.创建 channel
//        String channelId = "C-" + UUID.randomUUID().toString();
//        Channel channel = new Channel(channelId, this);
//        //2.将 channel 放入 Connection 管理的 channelMap 中
//        channelMap.put(channelId, channel);
//        //3.同时需要把创建 channel 的消息告知服务器
//        boolean ok = channel.createChannel();
//        if(!ok) {
//            //如果创建失败了，就需要把刚刚放入 channelMap 中的 channel 信息删除
//            channelMap.remove(channelId);
//            return null;
//        }
//        return channel;
//    }
//
//}

public class Connection {

    //一个 socket 连接管理多个 channel 通道
    private Socket socket = null;
    //管理的所有 channel 集合，key 是 channelId
    private ConcurrentHashMap<String, Channel> channelMap = new ConcurrentHashMap<>();
    //向服务器端的读取响应以及写入响应
    private InputStream inputStream = null;
    private OutputStream outputStream = null;
    private DataInputStream dataInputStream = null;
    private DataOutputStream dataOutputStream = null;
    //线程池处理回调
    private ExecutorService callbackPool = null;
    //扫描线程
    private Thread t = null;

    public Connection(String ip, int port) throws IOException {
        socket = new Socket(ip, port);
        inputStream = socket.getInputStream();
        outputStream = socket.getOutputStream();
        dataInputStream = new DataInputStream(inputStream);
        dataOutputStream = new DataOutputStream(outputStream);
        callbackPool = Executors.newFixedThreadPool(4);

        //使用一个扫描线程，检查当前流中是否服务器响应
        t = new Thread(() -> {
            while(!socket.isClosed()) {
                try {
                    //1.读取服务器响应
                    Response response = readResponse(dataInputStream);
                    //2.对不同响应进行不同处理
                    transChannel(response);
                } catch (SocketException e) {
                    System.out.println("[Connection] 客户端连接断开！ 地址: " + ip + ":" + port);
                } catch (IOException | MqException | ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
    }

    public void close() throws IOException {
        socket.close();
        callbackPool.shutdownNow();
        channelMap.clear();
    }


    /**
     * 读取响应
     * @param dataInputStream
     * @return
     */
    private Response readResponse(DataInputStream dataInputStream) throws IOException, MqException {
        int type = dataInputStream.readInt();
        int length = dataInputStream.readInt();
        byte[] payload = new byte[length];
        int n = dataInputStream.read(payload);
        if(n != length) {
            throw new MqException("[Connection] 读取响应数据异常！expectedLen=" +
                    length + "actualLen=" + n);
        }
        Response response = new Response();
        response.setType(type);
        response.setLength(length);
        response.setPayload(payload);
        return response;
    }

    /**
     * 写入请求
     */
    public void writeRequest(Request request) throws IOException {
        dataOutputStream.writeInt(request.getType());
        dataOutputStream.writeInt(request.getLength());
        dataOutputStream.write(request.getPayload());
        dataOutputStream.flush();
    }


    /**
     * 用来区分当前响应是用来区分控制请求的响应还是服务器那边推送过来的响应
     * @param response
     */
    private void transChannel(Response response) throws IOException, ClassNotFoundException, MqException {
        if(response.getType() == 0xc) {
            MessageCallback messageCallback = (MessageCallback)  BinaryTool.fromBytes(response.getPayload());
            Channel channel = channelMap.get(messageCallback.getChannelId());
            if(channel == null) {
                throw new MqException("[Connection] 当前 channel 不存在！");
            }
            //使用线程池来处理回调
            callbackPool.submit(() -> {
                try {
                    channel.getConsumer().handlerDelivery(messageCallback.getConsumerTag(),
                            messageCallback.getBasicProperties(), messageCallback.getBody());
                } catch (IOException | MqException e) {
                    throw new RuntimeException(e);
                }
            });
        } else {
            BasicResponse basicResponse = (BasicResponse) BinaryTool.fromBytes(response.getPayload());
            Channel channel = channelMap.get(basicResponse.getChannelId());
            if(channel == null) {
                throw new MqException("[Connection] 当前 channel 不存在！");
            }
            //将响应交给 channel 处理
            channel.handlerResponse(basicResponse.getRid(), basicResponse);
        }
    }

    /**
     * 创建 channel
     * @return
     */
    public Channel createChannel() throws IOException {
        //创建 channel 对象
        String channelId = "C-" + UUID.randomUUID().toString();
        Channel channel = new Channel(channelId, this);
        //把这个 channel 放入到 map 中
        channelMap.put(channelId, channel);
        //创建 channel 这个消息也要告诉服务器
        channel.createChannel();
        return channel;
    }

}
